-
Notifications
You must be signed in to change notification settings - Fork 837
test: add import compliance tests #4537
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
vms/evm/imports_test.go
Outdated
| isInEmulate := strings.Contains(path, "/vms/evm/emulate/") | ||
| isInVmsEvm := strings.Contains(path, "/vms/evm/") | ||
| isCoreth := strings.Contains(importPath, "/graft/coreth") | ||
| isSubnetEVM := strings.Contains(importPath, "/graft/subnet-evm") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(No action required) Not a biggie, but would prefer to see subnet-evm specific stuff as part of the migration not in advance.
But also, why is testing for these independent grafts conflated? Maybe implement 2 tests that use the same helpers, one for each graft location?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ARR4N suggested that all of this be combined into a single import violation test. Would you still like to see the tests separated out? I see the benefit of both ways
- single test: less code, everything in one place
- two tests: separation makes more sense semantically
I could also do a single test with two subtests?
vms/evm/imports_test.go
Outdated
| require.Fail(t, formatImportViolations(foundImports, header)) | ||
| } | ||
|
|
||
| // TestDoNotImportLibevmPseudo ensures that no code in the repository imports |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(No action required) If it's libevm-internal only, why isn't the package called internal so that the restriction is enforced automatically by golang?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ARR4N would this be possible?
vms/evm/imports_test.go
Outdated
| // - graft/coreth can be imported anywhere EXCEPT vms/evm (but vms/evm/emulate is an exception) | ||
| // - graft/subnet-evm cannot be imported anywhere EXCEPT vms/evm/emulate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these the rules? I understand emulate being allowed to import, but not the other limitations.
Rule of thumb for all code, not just this instance: comments should explain "why", not "what" nor "how".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a longer comment explaining why - is it satisfactory?
|
I think the entirety of this PR can be reduced to something like this: func TestImportViolations(t *testing.T) {
const root ".."
err := filepath.Walk(root, func(file string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
if strings.ToLower(filepath.Ext(file)) != "go" {
return nil
}
node, err := parser.ParseFile(token.NewFileSet(), file, nil, parser.ImportsOnly)
if err != nil {
return fmt.Errorf("parser.ParseFile(..., %q, ...): %v", file, err)
}
for _, spec := range node.Imports {
if spec.Path == nil {
continue
}
imp := strings.Trim(spec.Path.Value, `"`)
inGraft := strings.Contains(file, "/graft/")
inEmulate := strings.Contains(file, "/vms/evm/emulate/")
inVMsEVM := strings.Contains(file, "/vms/evm/")
importsPseudo := isPackageIn(imp, "github.com/ava-labs/libevm/libevm/pseudo")
importsCoreth := isPackageIn(imp, "github.com/ava-labs/avalanchego/graft/coreth")
importsSubnetEVM := isPackageIn(imp, "github.com/ava-labs/avalanchego/graft/subnet-evm")
// TODO: before merging, this needs a comment explaining why each of them are a violation
violations := []bool{
importsPseudo,
!inGraft && importsCoreth && inVMsEVM && !inEmulate,
!inGraft && importsSubnetEVM && !inEmulate,
}
if slices.Contains(violations, true) {
t.Errorf("File %q imports %q", file, imp)
}
}
return nil
})
require.NoErrorf(t, err, "filepath.Walk(%q)", root)
}
func isPackageIn(importPath, root string) bool {
if importPath == root {
return true
}
if len(importPath) < len(root)+1 {
return false
}
return strings.HasPrefix(importPath, root) && importPath[len(root)] == '/'
}I'm not particularly happy with the relative path to walk nor the way of checking if something is in a sub-dir (they can be fixed together), but otherwise I think this covers all bases. |
Co-authored-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Signed-off-by: Jonathan Oppenheimer <147infiniti@gmail.com>
I feel bad that you put all of these great comments together and then just gave me a much simpler test, but your diligence is appreciated. I have replaced the file with your suggested test (tweaked slightly) |
ARR4N
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GitHub is forcing me to add a comment here for no reason (well, no reason beyond that it's a terrible product).
| // TODO(jonathanoppenheimer): remove the emulate functionality once the emulate package is removed. | ||
| // TODO(jonathanoppenheimer): remove the graft functionality once the graft package will be removed. | ||
| func TestImportViolations(t *testing.T) { | ||
| var violations []string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why add the layer of indirection? Just report the violation when it arises.
FWIW this is why forcing use of require instead of judicious use of assert/require as appropriate is a bad idea. If multiple violations occur then it forces whack-a-mole fixing and rerunning of the test every time. Strict adherence to a style guide is not a valid reason to write more complicated code as it's antithetical to the purpose of a guide.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert was banned because in too many instances it was assumed to be equivalent to require (fail-fast) and this resulted in buggy tests. For those instances where multiple violations are possible, t.Log is always an option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also will catch all the violations no? Fill up the array first, and then check the array at the end -- so it won't have to be rerun a ton of times.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume he would prefer that the use of inline assertions so as to avoid the requirement to accumulate failures and output them at the end?
FWIW, I'm sympathetic to revisiting the assert ban, maybe something to bring up in retro? I would hope that we now have enough review maturity to avoid misuse going forward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yah, I understand. If we'd like to revisiting the ban, that totally makes sense, as it would certainly enable additional testing patterns, but I don't think this is the PR to do that in 😂. For retro!
vms/evm/imports_test.go
Outdated
| inGraft := strings.Contains(file, "/graft") | ||
| inEmulate := strings.Contains(file, "/vms/evm/emulate") | ||
| inVMsEVM := strings.Contains(file, "/vms/evm") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strings.Contains is subject to false positives. First find the repo root with filepath.Abs(root) and then check for prefixes with strings.HasPrefix(file, filepath.Join(repoRoot, "/graft")) etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've done so!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if this what you had in mind.
| // Check if importPath is a subpackage by ensuring it has the targetedImport prefix | ||
| // AND the next character is '/'. This is to prevent false positives where one | ||
| // package name is a prefix of another like "github.com/foo" vs "github.com/foobar". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // Check if importPath is a subpackage by ensuring it has the targetedImport prefix | |
| // AND the next character is '/'. This is to prevent false positives where one | |
| // package name is a prefix of another like "github.com/foo" vs "github.com/foobar". |
This just says what the code does so shouldn't be there as it's redundant and risks being or becoming incorrect. Heuristic: only explain "why" in inline comments, not how nor what. If a how or what is necessary then the code probably needs to be refactored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the what is necessary as Maru requested an explanation of the intent behind the code here: #4537 (comment).
Co-authored-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Signed-off-by: Jonathan Oppenheimer <147infiniti@gmail.com>
Co-authored-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Signed-off-by: Jonathan Oppenheimer <147infiniti@gmail.com>
Co-authored-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Signed-off-by: Jonathan Oppenheimer <147infiniti@gmail.com>
Why this should be merged
This PR accomplishes three things:
vms/evmdirectory from importing packages within thegraft/directory.libevm/pseudofrom being imported anywhere in AvalancheGoHow this works
The boundaries are as follows:
graft/corethcan be imported everywhere in AvalancheGo exceptvms/evm.vms/evm/emulateis an exception to this rulegraft/subnet-evmcan not be imported anywhere in AvalancheGovms/evm/emulateis an exception to this ruleHow this was tested
Existing CI. I also added a sample "bad" commit (404acd5) to show how CI would behave.

edit: this is no longer a good example and is out of date but I kept it to show the formatting -- pretend the file is in
vms/evm/sample_bad.go.Need to be documented in RELEASES.md?
No